using DocumentCreationSystem.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace DocumentCreationSystem.Services;

/// <summary>
/// Ollama服务实现
/// </summary>
public class OllamaService : IAIService
{
    private readonly IConfiguration _configuration;
    private readonly ILogger _logger;
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl;
    private readonly List<AIModel> _availableModels;
    private AIModel? _currentModel;

    public OllamaService(IConfiguration configuration, ILogger logger)
    {
        _configuration = configuration;
        _logger = logger;
        _httpClient = new HttpClient();
        
        _baseUrl = configuration["BaseUrl"] ?? "http://localhost:11434";
        _availableModels = new List<AIModel>();
        
        ConfigureHttpClient();
        _ = Task.Run(LoadAvailableModelsAsync);
    }

    private void ConfigureHttpClient()
    {
        _httpClient.DefaultRequestHeaders.Add("User-Agent", "DocumentCreationSystem/1.0");
        _httpClient.Timeout = TimeSpan.FromSeconds(120); // 120秒，符合API请求最佳实践
    }

    private async Task LoadAvailableModelsAsync()
    {
        try
        {
            var response = await _httpClient.GetAsync($"{_baseUrl}/api/tags");
            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                var result = JsonSerializer.Deserialize<OllamaModelsResponse>(json);
                
                if (result?.Models != null)
                {
                    _availableModels.Clear();
                    foreach (var model in result.Models)
                    {
                        _availableModels.Add(new AIModel
                        {
                            Id = model.Name,
                            Name = model.Name,
                            Provider = "Ollama",
                            Description = $"Ollama本地模型 - {model.Name}",
                            IsAvailable = true,
                            MaxTokens = 4096 // 默认值，实际可能不同
                        });
                    }
                    
                    if (_availableModels.Any())
                    {
                        _currentModel = _availableModels.First();
                        _logger.LogInformation($"Ollama加载了 {_availableModels.Count} 个模型");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "无法连接到Ollama服务");
        }
    }

    public async Task<List<AIModel>> GetAvailableModelsAsync()
    {
        // 如果模型列表为空，尝试重新加载
        if (!_availableModels.Any())
        {
            await LoadAvailableModelsAsync();
        }
        
        return _availableModels.ToList();
    }

    public async Task<bool> SetCurrentModelAsync(string modelId)
    {
        var model = _availableModels.FirstOrDefault(m => m.Id == modelId);
        if (model == null)
        {
            _logger.LogWarning($"Ollama模型不存在: {modelId}");
            return false;
        }

        // 检查模型是否可用
        try
        {
            var testRequest = new
            {
                model = modelId,
                prompt = "test",
                stream = false
            };

            var json = JsonSerializer.Serialize(testRequest);
            var content = new StringContent(json, Encoding.UTF8, "application/json");
            var response = await _httpClient.PostAsync($"{_baseUrl}/api/generate", content);
            
            if (response.IsSuccessStatusCode)
            {
                _currentModel = model;
                _logger.LogInformation($"切换到Ollama模型: {model.Name}");
                return true;
            }
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, $"Ollama模型不可用: {modelId}");
        }

        return false;
    }

    public AIModel? GetCurrentModel()
    {
        return _currentModel;
    }

    public async Task<string> GenerateTextAsync(string prompt, int maxTokens = 2000, float temperature = 0.7f)
    {
        if (_currentModel == null)
        {
            throw new InvalidOperationException("未设置当前Ollama模型");
        }

        try
        {
            var request = new
            {
                model = _currentModel.Id,
                prompt = prompt,
                stream = false,
                options = new
                {
                    temperature = Math.Max(0.1f, Math.Min(1.0f, temperature)),
                    num_predict = maxTokens
                }
            };

            var json = JsonSerializer.Serialize(request);
            var content = new StringContent(json, Encoding.UTF8, "application/json");

            var response = await _httpClient.PostAsync($"{_baseUrl}/api/generate", content);
            response.EnsureSuccessStatusCode();

            var responseJson = await response.Content.ReadAsStringAsync();
            var result = JsonSerializer.Deserialize<OllamaResponse>(responseJson);

            return result?.Response ?? "";
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Ollama文本生成失败");
            throw;
        }
    }

    public async Task<string> PolishTextAsync(string text, string style = "通用")
    {
        var prompt = $@"请对以下文本进行润色，要求：
1. 保持原文的核心意思不变
2. 提升文字的流畅性和可读性
3. 润色风格：{style}
4. 只返回润色后的文本，不要添加任何解释

原文：
{text}";

        return await GenerateTextAsync(prompt, 4000, 0.3f);
    }

    public async Task<string> ExpandTextAsync(string text, int targetLength, string? context = null)
    {
        var contextInfo = !string.IsNullOrEmpty(context) ? $"\n\n上下文信息：\n{context}" : "";
        
        var prompt = $@"请对以下文本进行扩写，要求：
1. 保持原文的核心内容和风格
2. 增加细节描述、情感表达和场景渲染
3. 目标长度约{targetLength}字
4. 确保扩写内容与原文自然衔接
5. 只返回扩写后的完整文本{contextInfo}

原文：
{text}";

        return await GenerateTextAsync(prompt, Math.Max(targetLength + 1000, 4000), 0.7f);
    }

    public async Task<string> GenerateChapterAsync(string outline, string? context = null, int targetWordCount = 6500)
    {
        var contextInfo = !string.IsNullOrEmpty(context) ? $"\n\n上下文信息：\n{context}" : "";
        
        var prompt = $@"请根据以下章节大纲创作小说章节内容。

=== 章节大纲 ===
{outline}

=== 字数要求（必须严格遵守）===
**目标字数：{targetWordCount}字**
- 最少不能低于{(int)(targetWordCount * 0.9)}字
- 最多不能超过{(int)(targetWordCount * 1.1)}字
- 请在创作过程中时刻关注字数，确保达到目标字数
- 如果内容不够，请增加细节描写、心理描写、环境描写等
- 如果内容过多，请适当精简，但不能删除关键情节

=== 创作要求 ===
1. 严格按照大纲内容进行创作
2. **必须达到{targetWordCount}字的目标字数，这是硬性要求**
3. 保持小说的连贯性和可读性
4. 注意人物性格和情节发展的一致性
5. 只返回章节正文内容，不要标题和其他说明
6. **在创作时要充分展开情节，增加对话、动作、心理、环境等描写来达到字数要求**{contextInfo}

=== 创作指导 ===
为了达到{targetWordCount}字的要求，请注意：
- 详细描写人物的外貌、表情、动作
- 丰富环境和场景的描述
- 增加人物内心独白和心理活动
- 扩展对话内容，让对话更自然生动
- 添加适当的背景信息和回忆片段
- 细致描写战斗、冲突等关键场面";

        return await GenerateTextAsync(prompt, Math.Max(targetWordCount + 2000, 8000), 0.8f);
    }

    public async Task<ConsistencyCheckResult> CheckConsistencyAsync(string currentText, string previousContext)
    {
        var prompt = $@"请检查以下当前文本与之前上下文的一致性，重点关注：
1. 人物性格和行为是否一致
2. 情节发展是否合理
3. 时间线是否正确
4. 设定是否矛盾

请简要分析并给出一致性评分（0-1），以及发现的问题和建议。

之前的上下文：
{previousContext}

当前文本：
{currentText}";

        try
        {
            var response = await GenerateTextAsync(prompt, 2000, 0.3f);
            
            // 简单的文本解析，提取一致性信息
            var isConsistent = !response.ToLower().Contains("不一致") && !response.ToLower().Contains("矛盾");
            var confidenceScore = ExtractConfidenceScore(response);
            
            return new ConsistencyCheckResult
            {
                IsConsistent = isConsistent,
                ConfidenceScore = confidenceScore,
                Issues = ExtractIssues(response),
                Suggestions = ExtractSuggestions(response)
            };
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Ollama一致性检查失败");
            return new ConsistencyCheckResult
            {
                IsConsistent = false,
                ConfidenceScore = 0.0f,
                Issues = new List<string> { $"检查过程出错: {ex.Message}" },
                Suggestions = new List<string> { "建议重新进行一致性检查" }
            };
        }
    }

    private float ExtractConfidenceScore(string response)
    {
        // 简单的评分提取逻辑
        if (response.Contains("很好") || response.Contains("完全一致"))
            return 0.9f;
        if (response.Contains("基本一致") || response.Contains("大体"))
            return 0.7f;
        if (response.Contains("部分") || response.Contains("轻微"))
            return 0.5f;
        if (response.Contains("不一致") || response.Contains("矛盾"))
            return 0.3f;
        
        return 0.6f; // 默认值
    }

    private List<string> ExtractIssues(string response)
    {
        var issues = new List<string>();
        var lines = response.Split('\n');
        
        foreach (var line in lines)
        {
            if (line.Contains("问题") || line.Contains("矛盾") || line.Contains("不一致"))
            {
                issues.Add(line.Trim());
            }
        }
        
        return issues.Any() ? issues : new List<string> { "未发现明显问题" };
    }

    private List<string> ExtractSuggestions(string response)
    {
        var suggestions = new List<string>();
        var lines = response.Split('\n');
        
        foreach (var line in lines)
        {
            if (line.Contains("建议") || line.Contains("应该") || line.Contains("可以"))
            {
                suggestions.Add(line.Trim());
            }
        }
        
        return suggestions.Any() ? suggestions : new List<string> { "保持当前写作方向" };
    }

    public async Task<string> GenerateOutlineAsync(string description, string outlineType)
    {
        var prompt = outlineType.ToLower() switch
        {
            "全书" => $@"请根据以下描述生成小说全书大纲，要求：
1. 包含主要情节线和人物关系
2. 分为若干卷或部分
3. 每部分包含主要事件和发展
4. 保持情节的连贯性和吸引力

描述：{description}",

            "卷宗" => $@"请根据以下描述生成卷宗大纲，要求：
1. 详细的章节安排
2. 每章的主要内容和目标
3. 人物发展和情节推进
4. 保持节奏感和悬念

描述：{description}",

            "章节" => $@"请根据以下描述生成章节细纲，要求：
1. 详细的场景安排
2. 人物对话和行动
3. 情节发展的关键点
4. 字数约6500字的内容规划

描述：{description}",

            _ => $@"请根据以下描述生成{outlineType}大纲：

描述：{description}"
        };

        return await GenerateTextAsync(prompt, 4000, 0.8f);
    }

    public async Task<List<CharacterInfo>> ExtractCharacterInfoAsync(string text)
    {
        var prompt = $@"请从以下文本中提取角色信息，包括：
- 角色名称
- 角色描述
- 角色属性（如境界、年龄等）
- 技能列表
- 装备列表

请按以下格式输出每个角色：
角色名：[名称]
描述：[描述]
属性：[属性1]=[值1], [属性2]=[值2]
技能：[技能1], [技能2]
装备：[装备1], [装备2]

文本内容：
{text}";

        try
        {
            var response = await GenerateTextAsync(prompt, 3000, 0.3f);
            return ParseCharacterInfo(response);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Ollama角色信息提取失败");
            return new List<CharacterInfo>();
        }
    }

    private List<CharacterInfo> ParseCharacterInfo(string response)
    {
        var characters = new List<CharacterInfo>();
        var lines = response.Split('\n');
        CharacterInfo? currentCharacter = null;

        foreach (var line in lines)
        {
            var trimmedLine = line.Trim();

            if (trimmedLine.StartsWith("角色名：") || trimmedLine.StartsWith("角色名:"))
            {
                if (currentCharacter != null)
                {
                    characters.Add(currentCharacter);
                }

                currentCharacter = new CharacterInfo
                {
                    Name = trimmedLine.Substring(trimmedLine.IndexOf('：') + 1).Trim()
                };
                if (string.IsNullOrEmpty(currentCharacter.Name))
                {
                    currentCharacter.Name = trimmedLine.Substring(trimmedLine.IndexOf(':') + 1).Trim();
                }
            }
            else if (currentCharacter != null)
            {
                if (trimmedLine.StartsWith("描述：") || trimmedLine.StartsWith("描述:"))
                {
                    currentCharacter.Description = trimmedLine.Substring(trimmedLine.IndexOf('：') + 1).Trim();
                    if (string.IsNullOrEmpty(currentCharacter.Description))
                    {
                        currentCharacter.Description = trimmedLine.Substring(trimmedLine.IndexOf(':') + 1).Trim();
                    }
                }
                else if (trimmedLine.StartsWith("属性：") || trimmedLine.StartsWith("属性:"))
                {
                    var attributesStr = trimmedLine.Substring(trimmedLine.IndexOf('：') + 1).Trim();
                    if (string.IsNullOrEmpty(attributesStr))
                    {
                        attributesStr = trimmedLine.Substring(trimmedLine.IndexOf(':') + 1).Trim();
                    }

                    var attributes = attributesStr.Split(',');
                    foreach (var attr in attributes)
                    {
                        var parts = attr.Split('=');
                        if (parts.Length == 2)
                        {
                            currentCharacter.Attributes[parts[0].Trim()] = parts[1].Trim();
                        }
                    }
                }
                else if (trimmedLine.StartsWith("技能：") || trimmedLine.StartsWith("技能:"))
                {
                    var skillsStr = trimmedLine.Substring(trimmedLine.IndexOf('：') + 1).Trim();
                    if (string.IsNullOrEmpty(skillsStr))
                    {
                        skillsStr = trimmedLine.Substring(trimmedLine.IndexOf(':') + 1).Trim();
                    }

                    var skills = skillsStr.Split(',');
                    currentCharacter.Skills.AddRange(skills.Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)));
                }
                else if (trimmedLine.StartsWith("装备：") || trimmedLine.StartsWith("装备:"))
                {
                    var equipmentStr = trimmedLine.Substring(trimmedLine.IndexOf('：') + 1).Trim();
                    if (string.IsNullOrEmpty(equipmentStr))
                    {
                        equipmentStr = trimmedLine.Substring(trimmedLine.IndexOf(':') + 1).Trim();
                    }

                    var equipment = equipmentStr.Split(',');
                    currentCharacter.Equipment.AddRange(equipment.Select(e => e.Trim()).Where(e => !string.IsNullOrEmpty(e)));
                }
            }
        }

        if (currentCharacter != null)
        {
            characters.Add(currentCharacter);
        }

        return characters;
    }
}

/// <summary>
/// Ollama模型列表响应
/// </summary>
public class OllamaModelsResponse
{
    [JsonPropertyName("models")]
    public OllamaModel[]? Models { get; set; }
}

public class OllamaModel
{
    [JsonPropertyName("name")]
    public string Name { get; set; } = string.Empty;
}

/// <summary>
/// Ollama生成响应
/// </summary>
public class OllamaResponse
{
    [JsonPropertyName("response")]
    public string? Response { get; set; }
}
